/* $Id: Diet3DCanvas.java.in,v 1.18 2009-01-12 09:46:33 rzr Exp $ */
#ifndef Diet3DCanva_java_in_
#define Diet3DCanva_java_in_
/**
 * @author www.Philippe.COVAL.free.fr
 * Copyright and License : http://rzr.online.fr/license.htm
 **/
#include "HackJavaCpp.java.in"

#ifndef EXEN
#define getTimerTickCount() System.currentTimeMillis()
//#undef MACRO_println
//#define MACRO_println System.out.println
#endif

#ifndef NO_INLINE
# define INCLUDE_Diet3D //crash the system
#endif

#ifdef WANT_TRACE
# define trace(x) mRender.trace(x)
#else
# define trace(x) {}
#endif
/**
 * @author www.Philippe.COVAL.free.fr
 * Copyright and License : http://rzr.online.fr/license.htm
 **/
public final class Diet3DCanvas

#ifdef MIDP_2_0
extends GameCanvas
#elif defined EXEN
extends Gamelet
#elif defined ANDROID // SurfaceView widget
extends SurfaceView 
#else
extends Canvas
#endif //MIP20, EXEN, ANDROID, default

#ifdef ANDROID // widgets thread
implements SurfaceHolder.Callback , Runnable
#elif ! defined EXEN
implements Runnable 
#endif
{

#ifndef INCLUDE_Diet3D
  public Diet3D mRender = null; 
#else
# include "Diet3D.java.in"
# define   mRender this
#endif

  public boolean isStarted_ = false;
  /** ms per frame */
  static final int mspf = 40; // +500 //40=25fps
  static int fps = 25;
  // event
  static final int tickCanvas = 2000;

  static long time = 0;
  static long last = 0;
  static int initCanvas = 0;


#ifdef ANDROID
  SurfaceHolder mSurfaceHolder_ = null;
  // Context context_ = null;
  Thread mThread = null ;
#else

#if defined EXEN
  Thread mThread = null ;
#endif

  Thread mThread = null ;

#endif


#ifndef HAVE_DISPLAY_NO
  Display mDisplay = null;
#endif

#ifndef HAVE_IMAGE_NO   
  Image moff = null;
#endif

  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  //### contructor
#ifdef ANDROID
  public Diet3DCanvas(Context context)
  {
    super(context);
#ifndef INCLUDE_Diet3D
    mRender = new Diet3D( getWidth() , getHeight() );
#else
    mRender.init( getWidth() , getHeight() ); // possible bug here // CHK
    //init(); //crash the phone
#endif
    trace("w=" + getWidth() );
    mSurfaceHolder_ = getHolder();
    mSurfaceHolder_.addCallback(this);
    // init();
  }
#else
  public Diet3DCanvas() 
  {
#ifdef MIDP_2_0
    super(true); // possible bug here // CHECK!!!
#endif
#ifndef INCLUDE_Diet3D
    mRender = new Diet3D( getWidth() , getHeight() );
#else
    mRender.init( getWidth() , getHeight() ); // possible bug here // CHK
    //init(); //crash the phone
#endif

#if ( (defined MIDP_1_0 ) ) // || (defined MIDP_2_0 ) )     
    if ( ! isDoubleBuffered() ) {  //BUG
      moff = Image.createImage( getWidth() , getHeight());
      //MACRO_println("not double buffer"); 
    }
#endif
  }
#endif

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // ### missings funct

#ifdef EXEN
  public void onInit() //EXEN
  { 
    time = getTimerTickCount();
    last = time;
    startTimer(100);
    repaint(); //??
  }
#endif

#ifdef ANDROID
  static Canvas canvas_ = null;

  public void repaint()
  {
#if ! defined ANDROID //TODO: crash on android on repaint 
    if ( canvas_ != null ) paint( canvas_ );
#else
	  Canvas g = null;
	  try {
          g = mSurfaceHolder_.lockCanvas(null);
          synchronized (mSurfaceHolder_) {
        	    mRender.paint(g); //  +--javax.microedition.lcdui.Graphics

          }          } finally {
              // do this in a finally so that if an exception is thrown
              // during the above, we don't leave the Surface in an
              // inconsistent state
              if (g != null) {
            	  mSurfaceHolder_.unlockCanvasAndPost(g);
              }
          }
#endif
  }

//  @Override
    protected void onDraw(Canvas canvas)
    {
      super.onDraw(canvas);
      paint(canvas);
      canvas_ = canvas;
    }

  public void surfaceCreated(SurfaceHolder holder) {
	  start();
  }
  public void surfaceDestroyed(SurfaceHolder holder) {
  }
	/* Callback invoked when the surface dimensions change. */
  public void surfaceChanged(SurfaceHolder holder, int format, int width,
          int height) 
  {
  }

#endif


  //#define INCLUDE
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  public void update(Graphics g) 
  { 
    paint(g); 
  }

#ifndef INCLUDE_Diet3D

#if ( defined MIDP_1_0 )
  protected
#else // ( ( defined MIDP_2_0 ) || ( defined EXEN ) || (defined AWT ) || (defined NTTDOCOMO )) 
    public  
#endif // pub
    void paint(Graphics g)  //EXEN
  {
    //if ( initCanvas == 0 )
    // Pretty strange bug // poco then kern-exec 3 
    //if ( initCanvas == 0 )  // 60 //40=bug // 80-
#if ( (defined MIDP_1_0 )  ||(defined MIDP_2_0 )  || (defined NOKIA ) ) 
    g.setClip(0, 0, getWidth(), getHeight());
#endif

#if ( (defined MIDP_1_0 ) ) //|| (defined MIDP_2_0 ) )     
    if( ( moff != null ) &&  ( g != moff.getGraphics() ) ) {
      paint( moff.getGraphics() );
      g.drawImage( moff, 0 , 0 , 0 );
      return ;
    }
#endif

#if ( (defined MIDP_2_0 )  || (defined NOKIA ) ) 
    //TODO see isMutable && != null
    for(initCanvas=0; //(bug on nokia 6600 40<70)
        ( initCanvas<70 ) || ( g == null ) ;
        initCanvas++)   //( ! g.isMutable() ) ||
      g.setColor(255,255,255); 
#elif defined EXEN
    g.clearRect(0,0, width, height); //TODO opt w/ bbox
#endif

    mRender.paint(g); //  +--javax.microedition.lcdui.Graphics

    if ( tickCanvas < 100 ) { paintInfo(g);}//TODO

#ifdef WANT_INFO
    paintInfo(g);
#endif

#ifdef EXEN
    update();
#endif        

#ifdef MIPD_2_0
    //flushGraphics(); //@midp2
#endif
  }
#endif
    
  public void onTimerTick() //EXEN
  {
    // MACRO_println(".time=" +time + " / last=" + last + " r=" + ( time - last) );        
    time = getTimerTickCount();
        
    if ( ( time - last )  >  tickCanvas ) { last = time; mRender.tick(); }
#ifdef MIDP_2_0
    input();
#endif
    mRender.update();        
#if 0 //def MIDP_2_0
    Graphics g = getGraphics(); //is it better ?
    paint(g);
#else
    repaint();
#endif

    //file:///opt/WTK2.1/docs/api/midp/javax/microedition/lcdui/Display.html#callSerially(java.lang.Runnable)

    // 6600 = cs !sr 
    // 5100 != sr + cr , sr +!cr = crash zsort
    // ri midp2 : !sr + cr = missing parts
    // ri midp2 : sr !cs = !timer
    //#if ( ( ! defined MIDP_2_0 )  &&  ( ( defined NOKIA ) || ( defined MIDP_1_0 ) ))



#if ( ( defined MIDP_2_0 )  || ( defined NOKIA ) || ( defined MIDP_1_0 ) )
    serviceRepaints(); //flush and block //BUG //OK @6600 3560
    //mDisplay.callSerially(mThread); // TIMERBUG //ok 6600 3560
#endif // BUG

  }
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifdef ANDROID
    static Paint paint_static = new Paint(); //TODO
#endif

  public void paintInfo(Graphics g)
  {
      String s = "Diet3D" + "\n" 
	  + "FPS=" + fps + "\n" 
	  + "www.rzr.online.fr" + "\n" ;
      //	  + vmode[mode]; //TODO
#ifdef ANDROID //TODO
    paint_static.setColor(COLOR_FG); 
    g.drawText( s , getWidth()>>1, 40 , paint_static); 
#elif ! ( (defined AWT ) || ( defined EXEN ) || ( defined NTTDOCOMO )  )
    g.setColor( 0x007F7F7F ); // BUG
    g.drawString( "Diet3D " , 0, 0, g.TOP | g.LEFT );
    // g.drawString( "Diet3D FPS=" + fps, 0, 0, g.TOP | g.LEFT );

    g.drawString( "www.rzr.free.fr", 
                  getWidth() , getHeight()  , g.BASELINE  | g.RIGHT);
#else
    // TODO
#endif
  }

  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void intro()
{
#ifdef ANDROID
    // paintInfo( canvas_ );
//TODO: sleep
#endif

}
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifdef EXEN

  public int getHeight() { return this.height; }

  public int getWidth() { return this.width; }

#else

  public void start() 
  {
    isStarted_ = true;
    //if ( t == null )   
    time = getTimerTickCount();
    last = time;

    mThread = new Thread(this);
    if (mThread != null ) mThread.start();
  }
  
  public void stop() 
  {
    isStarted_ = false; 
    mThread = null ; //.stop();
  }

  public void toggle() 
  {
    isStarted_ = ! isStarted_ ; 
  }

  public void run() 
  {
    while ( isStarted_ && ( Thread.currentThread() == mThread ) ) {
      onTimerTick(); //paint etc
      time = ( getTimerTickCount() - time );
      if (  time <= 0  ) time = 1;
      fps = (int) 1000 / (int) time ;
      // trace( "time=" + time + "\n");
      if ( time <= mspf ) { time = mspf - time ; } else time=1;
      try { Thread.sleep(time); // may unlock 
      } catch (InterruptedException ex) { ex.printStackTrace(); }
    }
  }
#endif //EXEN

  // --- key -----------------------------------------------------------------

#if ( ! ( (defined AWT ) || ( defined NTTDOCOMO) || (defined ANDROID ) ))

#ifdef EXEN
  public void onKeyPress(int keyCode)
#else
    protected void keyPressed(int keyCode) // KeyReleased
#endif
  { 
    switch(getGameAction(keyCode)) { //MIDP1.0
    case LEFT:
      //mRender.ry_ =  1; //
      mRender.ry_++;
      break;
    case RIGHT:
      //mRender.ry_ = -1; //
      mRender.ry_--;
      break;
    case UP:
      //mRender.rx_ =  1;//
      mRender.rx_++;
      break;
    case DOWN:
      //mRender.rx_ = -1;//
      mRender.rx_--;
      break;

    case FIRE:
      if ( mRender.ry_ != 0 ) { mRender.ry_ = 0; return ; } 
      if ( mRender.rx_ != 0 ) { mRender.rx_ = 0; return ; } 
      if ( mRender.rz_ != 0 ) { mRender.rz_ = 0; return ; } 
      if ( ( mRender.rx_ == 0 ) &&
           ( mRender.ry_ == 0 ) &&
           ( mRender.rz_ == 0 ) ) {
        mRender.reset();
        mRender.rx_ = mRender.ry_ = mRender.rz_ = -1;
      } 
      break;
    case KEY_NUM0:
      mRender.toggleMode( 0);
      break;
    case KEY_NUM1:
      mRender.toggleMode( 1);
      break;
    case KEY_NUM2:
      mRender.toggleMode( 2);
      break;
    case KEY_NUM3:
      mRender.toggleMode( 3);
      break;
    case KEY_NUM4:
      mRender.toggleMode( 4);
      break;
    case KEY_NUM5:
      mRender.toggleMode( 5);
      break;
    case KEY_NUM6:
      mRender.toggleMode(6);
      break;
    case KEY_NUM7:
      mRender.toggleMode( 7);
      break;
    case KEY_NUM8:
      mRender.toggleMode( 8);
      break;
    case KEY_NUM9:
      mRender.toggleMode( 9);
      break;

    case KEY_POUND:
    case GAME_A:
      //mRender.toggleShape();
      break;

    case KEY_STAR: //WTK2.1 : 1 3 7 9 + on off
    case GAME_B:
      mRender.toggleMode();
      break;

    case GAME_C:
    case GAME_D:
      mRender.toggleAnim();
      break;
            
    default:
      if (keyCode > 0 ) {
        mRender.toggleMode();
      } else {
        mRender.toggleShape();
#ifdef EXEN
        mRender.toggleMode();
#endif
      }
      break;
    }
    //MACRO_println(""+keyCode);
  }

#ifdef MIDP_2_0 //dont work w/ wtk2.1
  private void input()
  {
    int keyStates = getKeyStates();
        
    if ((keyStates & LEFT_PRESSED) != 0) 
      mRender.ry_++; 
    //mRender.ry_=1;
    else if ((keyStates & RIGHT_PRESSED) != 0) 
      mRender.ry_--; 
    //mRender.ry_=-1;
    else if ((keyStates & UP_PRESSED) != 0)
      mRender.rx_++;
    //mRender.rx_=1;
    else if ((keyStates & DOWN_PRESSED) != 0)
      mRender.rx_--; 
    //mRender.rx_=-1;
    else if ((keyStates & FIRE_PRESSED) != 0) {
      if ( mRender.ry_ != 0 ) { mRender.ry_ = 0; return ; } 
      if ( mRender.rx_ != 0 ) { mRender.rx_ = 0; return ; } 
      if ( mRender.rz_ != 0 ) { mRender.rz_ = 0; return ; } 
      if ( ( mRender.rx_ == 0 ) &&
           ( mRender.ry_ == 0 ) &&
           ( mRender.rz_ == 0 ) ) {
        mRender.rx_ = mRender.ry_ = mRender.rz_ = 1;
      } 
    } else {
      //mRender.toggleMode();
    }
  } 
#endif  //MIDP2
#endif    // #ifndef AWT
}


#if 0
run()
{
  //mTimer.scheduleAtFixedRate( mTick , 100 , 100);
}
Timer  mTimer = new Timer();
Tick mTick = new Tick();

final class Tick extends TimerTask 
{
  public void run()
  {
    //try { wait(); } catch (Exception e) {}        
    mCanvas.repaint();
    mCanvas.mRender.update();        
    //notifyAll();
  }
}  
#endif

#endif // Diet3DCanva_java_in_

/*#eof "$Id: Diet3DCanvas.java.in,v 1.18 2009-01-12 09:46:33 rzr Exp $" */
